/**
 * Copyright [2010] Delta Air Lines, Inc. All Rights Reserved Access,
 * Modification, or Use Prohibited Without Express Permission of Delta Air
 * Lines.
 */
package com.delta.rafg.core.base;


import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlType;
import javax.xml.namespace.QName;

import com.delta.rafg.core.AObjectHelperImpl;
import com.delta.rafg.core.log.RAFGLogger;
import com.delta.rafg.coredata.AObject;
import com.delta.rafg.coredata.ARequest;


/**
 * AWorkerBean.java: used for utility purpose .
 * @author Delta
 * @version 1.0
 * @see : AWorkerBean.
 */
public abstract class AWorkerBean extends AObject implements IWorkerBean {

    /**
     * Holds the value of serialVersionUID property.
     */
    private static final long serialVersionUID = 1L;
    
    protected static boolean stackTraceOff = false;
    protected static boolean stackTraceFull = false;
    static {
        if (getaObj() == null) {
            setaObj(new AObjectHelperImpl());
        }
        
        String stackTraceOffStr = System.getProperty("com.delta.rafg.logging.stacktraceoff");
        stackTraceOff = (stackTraceOffStr != null && stackTraceOffStr.equalsIgnoreCase("true"));

        String stackTraceFullStr = System.getProperty("com.delta.rafg.logging.stacktracefull");
        stackTraceFull = (stackTraceFullStr != null && stackTraceFullStr.equalsIgnoreCase("true"));

    
    }

    /**
     * This method is used for printing exception message on console
     * @param pText holds String object
     * @param e holds Throwable object
     */
    protected static void printStackTrace(final String pText, final Throwable e) {
    	try{
    		
    		if(RAFGLogger.shouldLog(RAFGLogger.WARNING)){
    			printFullStackTrace(pText, e);
    		}
    		else if(RAFGLogger.shouldLog(RAFGLogger.ERROR_ONLY)){
	            final StackTraceElement stackTrace = e.getStackTrace()[0];
	            final String theFileName = stackTrace.getFileName();
	            if( theFileName != null && !theFileName.isEmpty() && stackTrace.getLineNumber()>0 ){
		            String errRes = e.getMessage();
		            errRes = errRes == null ? e.toString() : errRes;

		            RAFGLogger.error(pText + ": " + e.getClass().getName() + " ("
		                    + errRes + ")" + " at " 
		                	+ (stackTrace.getClassName().substring(stackTrace.getClassName().lastIndexOf(".")+1)) + "."
		                    + stackTrace.getMethodName() + " (" + stackTrace.getFileName()
		                    + ":" + stackTrace.getLineNumber() + ")");
	            }
	            else{
	            	printFullStackTrace(pText, e);
	            }
	    	}
	    	
		}
		catch(Throwable t){}
    }
    
    private static void printFullStackTrace(final String pText, final Throwable e){
    	try{
        	if( pText != null && !pText.isEmpty() ){
        		RAFGLogger.error(pText);
        	}
        	ByteArrayOutputStream baos = new ByteArrayOutputStream();
        	e.printStackTrace(new PrintStream(baos));
    		RAFGLogger.error(baos.toString());
    		baos.close();
    	}
    	catch(Throwable t){}
    }

    /**
     * This method is used for printing message on console
     * @param e holds Throwable object
     */
    protected static void printStackTrace(final Throwable e) {

        printStackTrace("", e);
    }

    /**
     * This method is used for checking null condition. If String is not null
     * then it will change String into Upper case.
     * @param pString
     * @return String
     */
    public String checkNull(final Object pString) {

        if (pString == null) {
            return "";
        } else {
            return String.valueOf(pString).toUpperCase();
        }
    }

    /**
     * This method is used for checking null condition. If String is not null
     * then it will change String into Upper case and return substring from
     * input parameter index
     * @param pString
     * @param pStartIndex
     * @param pEndIndex
     * @return String
     */
    public String checkNull(final String pString, final int pStartIndex,
        final int pEndIndex) {

        String theStr = checkNull(pString);
        if (theStr.length() > pEndIndex && pStartIndex >= 0
            && pEndIndex > pStartIndex) {
            theStr = theStr.substring(pStartIndex, pEndIndex);
        }
        return theStr;
    }

    /**
     * This method is used for getting browser information
     * @param request
     * @return
     */
    protected static String getBrowserInfo(final HttpServletRequest request) {

        final String ua = request.getHeader("User-Agent");
        final boolean isFirefox = ua != null && ua.indexOf("Firefox/") != -1;
        final boolean isMSIE = ua != null && ua.indexOf("MSIE") != -1;
        String theBrowser = "";
        if (isMSIE) {
            theBrowser = "IE";
        } else if (isFirefox) {
            theBrowser = "Firefox";
        }
        return theBrowser;
    }

    /**
     * This method is used for setting request info into the HttpServletRequest
     * @param pRafgRequest
     * @param pRequest
     */
    protected static void setHttpData(final ARequest pRafgRequest,
        final HttpServletRequest pRequest) {
        pRafgRequest.setHttpData(getBrowserInfo(pRequest), pRequest
            .getRemoteHost(), pRequest.getSession().getId(), pRequest
            .getLocale().getLanguage());
    }
    
	/**
	 * @param pRafgRequest
	 * @param pRequest
	 */
	public static void copyHttpRequest(final ARequest pRafgRequest,
	        final HttpServletRequest pRequest) {

		if (pRafgRequest != null && pRequest != null) {
			setHttpData(pRafgRequest, pRequest);
		}
	}
	
    /**
     * This will return the child, grand child, or great grand child and so on
     *  attribute from an object.
     *  Example: getDataFromObject(aGreatGrandpaObject, "Grandpa.Papa.Kid.Name")
     *  	will return aGreatGrandpaObject.getGrandpa().getPapa().getKid().getName()
     *  
     * 	A more useful Example:
     * 		getDataFromObject(aBusinessFault, "FaultInfo.DetailedInfo.ErrorMessage")
     * 		You can do aBusinessFault.getFaultInfo().getFaultDetailedInfo().getErrorMessage()
     * 		irrespective of the version of BusinessFault. 
     * 
     * @param pObject
     * @param pAttribute
     * @return
     */
    public Object getDataFromObject(Object pObject, String pAttribute ){
    	
    	if(pObject == null || pAttribute == null )
    		return "NULL";
    	
		// Use aObject as the place holder for each object down rabbit hole.
		Object aObject = pObject;
		String theAttribute = pAttribute;
		
		// the idea is to break down nested attributes. E.g.: Grandpa.Papa.Kid.Name
		// 	It will do aObject.getGrandpa() ==> pResponse.getGrandpa()
		//		then aObject.getPapa() ==> pResponse.getGrandpa().getPapa()
		//			then aObject.getKid() ==> pResponse.getGrandpa().getPapa().getKid()
		//				then aObject.getName()	==> pResponse.getGrandpa().getPapa().getKid().getName()		
		StringTokenizer aSt = new StringTokenizer(theAttribute, ".");
		while (aSt.hasMoreTokens()) {
			String aClass = aSt.nextToken();
			try {
				aObject = aObject.getClass().getMethod("get" + aClass)
						.invoke(aObject, (Object[]) null);
			} catch (Exception e) {
				return "NOT FOUND: " + pObject.getClass().getSimpleName() + pAttribute;
			}
		}
		
		//aObject should probably have pResponse.getGrandpa().getPapa().getKid().getName()
		return aObject;
    }
    
    
	static Map<String, JAXBContext> jaxbContextPool = new ConcurrentHashMap<String, JAXBContext>(150);
	
	public JAXBContext getJAXBContext(Object pObject) throws JAXBException{
		String className = "";
		
		if (pObject != null) {
			className = pObject.getClass().getName();
		}
		else {
			className = ABackendInterface.class.getName();
		}
		
		JAXBContext theJC = jaxbContextPool.get(className);
		
		if (theJC == null) {
			theJC = JAXBContext.newInstance(pObject.getClass());
			jaxbContextPool.put(className, theJC);
		}
		return theJC;
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public String getSoapString(Object pObject){

		if(pObject == null)
			return "null";

		String retVal = "";
		try{
			
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
	        JAXBContext jc = getJAXBContext(pObject);
			Marshaller marshaller = jc.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
			//marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

			String theObjectName = pObject.getClass().getSimpleName();
			if(theObjectName.endsWith("Type")){
				theObjectName = theObjectName.substring(0, theObjectName.length()-4);
			}

			marshaller.marshal(new JAXBElement(new QName(getNameSpaceAnnotation(pObject),theObjectName),
					pObject.getClass(), pObject), baos);
			retVal += new String(baos.toByteArray());
			retVal = maskAttribute(retVal, "Password");
			retVal = maskAttribute(retVal, "password");
			retVal = maskAttribute(retVal, "PASSWORD");
			retVal = maskAttribute(retVal, "FormOfPayment");
			retVal = maskAttribute(retVal, "formofpayment");
		} catch (Exception e) {
			
			RAFGLogger.infoException(e);
		}

		return retVal;
	}
	
	private String getNameSpaceAnnotation(Object pObject){
		String retAnno = "NS";
		Annotation[] theAnnos = pObject.getClass().getAnnotations();
		for(Annotation theAnno : theAnnos){
			if(theAnno instanceof XmlType){
				retAnno = ((XmlType)theAnno).namespace();
				break;
			}
		}
		return retAnno;
	}	
	
	private String maskAttribute(String pString, String pAttribute){

		String theRetStr = pString;

		String theAttribute = pAttribute + "=\"";
		String theEndAttribute = "\"";
		int attrPos = theRetStr.indexOf(theAttribute);
		if(attrPos>-1){
			int endPos = theRetStr.indexOf(theEndAttribute, attrPos + theAttribute.length());
			String theSubst = theRetStr.substring(attrPos + theAttribute.length(), endPos);
			if (theSubst != null && !theSubst.isEmpty())
				theRetStr = theRetStr.replace(theSubst, "********");
		}

		theAttribute = "<" + pAttribute + ">";
		theEndAttribute = "</" + pAttribute + ">";
		attrPos = theRetStr.indexOf(theAttribute);
		if(attrPos>-1){
			int endPos = theRetStr.indexOf(theEndAttribute, attrPos + theAttribute.length());
			String theSubst = theRetStr.substring(attrPos + theAttribute.length(), endPos);
			if (theSubst != null && !theSubst.isEmpty())
				theRetStr = theRetStr.replace(theSubst, "********");
		}

		return theRetStr ;
	}
	
	public String getSoapRequestString(Object pObject){
		String retVal = "\n";
		retVal += "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header/><soapenv:Body>";
		retVal += getSoapString(pObject);
		retVal += "</soapenv:Body></soapenv:Envelope>\n";
		return retVal;
	}
	
    
}
